package com.tbb.app.base.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tbb.app.base.dto.NoteAddDTO;
import com.tbb.app.base.entity.Channel;
import com.tbb.app.base.entity.Note;
import com.tbb.app.base.entity.User;
import com.tbb.app.base.mapper.NoteMapper;
import com.tbb.app.base.search.NoteAdminPageSearch;
import com.tbb.app.base.search.NotePageSearch;
import com.tbb.app.base.search.NoteUserPageSearch;
import com.tbb.app.base.service.*;
import com.tbb.app.base.vo.NoteAdminDetailVO;
import com.tbb.app.base.vo.NoteAdminVO;
import com.tbb.app.base.vo.NoteVO;
import com.tbb.app.base.vo.SimpleVO;
import com.tbb.app.common.entity.PageBeanSearch;
import com.tbb.app.common.exception.BusinessException;
import com.tbb.app.common.service.CacheService;
import com.tbb.app.common.service.CurrentLoginUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 笔记表 服务实现类
 *
 * @author Liu
 * @since 2022-01-11
 */
@Service
public class NoteServiceImpl extends ServiceImpl<NoteMapper, Note> implements INoteService {

    @Autowired
    private IUserService iUserService;
    @Autowired
    private CurrentLoginUserService currentLoginUserService;
    @Autowired
    private IFollowService iFollowService;
    @Autowired
    private IUserNoteService iUserNoteService;
    @Autowired
    private IChannelService iChannelService;
    @Autowired
    private CacheService cacheService;

    @Override
    public IPage<NoteVO> page(NotePageSearch search, boolean isSearch) {

        IPage<Note> page = new Page<>(search.getCurrentPage(), search.getPageSize());
        QueryWrapper<Note> wrapper = new QueryWrapper<>();
        wrapper.lambda().orderByDesc(Note::getCreateTime)
                .eq(Note::getIsPublish, true);
        if (!StringUtils.isEmpty(search.getChannelId())) {
            wrapper.lambda().eq(Note::getChannelId, search.getChannelId());
        }
        if (!StringUtils.isEmpty(search.getTitle())) {
            if (isSearch) {
                List<String> history = iUserService.listSearch();
                if (!history.contains(search.getTitle())) {
                    history.add(search.getTitle());
                    cacheService.set(currentLoginUserService.getUserId(true), history, 7, TimeUnit.DAYS);
                }
            }
            wrapper.lambda().like(Note::getTitle, search.getTitle());
        }

        IPage<Note> result = this.page(page, wrapper);
        Map<String, User> userMap = iUserService.getByIds(result.getRecords().stream().map(Note::getUserId).collect(Collectors.toList()))
                .stream().collect(Collectors.toMap(User::getId, Function.identity()));
        List<String> notesLike = iUserNoteService.listLikes(result.getRecords().stream().map(Note::getId).collect(Collectors.toList()));

        return result.convert(n -> {
            NoteVO vo = new NoteVO(n);
            User user = userMap.getOrDefault(n.getUserId(), new User());
            vo.setUserImg(user.getProfileImg());
            vo.setUserAccount(user.getAccount());
            vo.setUserName(user.getName());
            vo.setIsLiked(notesLike.contains(n.getId()));
            return vo;
        });
    }

    @Override
    public IPage<Note> pageVideo(NotePageSearch search, boolean isSearch) {
        IPage<Note> page = new Page<>(search.getCurrentPage(), search.getPageSize());
        QueryWrapper<Note> wrapper = new QueryWrapper<>();
        wrapper.lambda().orderByDesc(Note::getCreateTime).eq(Note::getIsPublish, true).eq(Note::getChannelId, "1003");

        return this.page(page, wrapper);
    }

    @Override
    public IPage<NoteVO> page(NoteUserPageSearch search) {
        IPage<Note> page = new Page<>(search.getCurrentPage(), search.getPageSize());
        QueryWrapper<Note> wrapper = new QueryWrapper<>();
        wrapper.lambda().orderByDesc(Note::getCreateTime)
                .eq(Note::getIsPublish, true);

        String userId = StringUtils.isEmpty(search.getUserId()) ? currentLoginUserService.getUserId(true) : search.getUserId();
        if (search.getType() == 0) {
            wrapper.lambda().eq(Note::getUserId, userId).notIn(Note::getChannelId,"1003");
        } else {
            List<String> noteIds = iUserNoteService.listCollects(userId);
            if (CollectionUtils.isEmpty(noteIds)) {
                return new Page<>(search.getCurrentPage(), search.getPageSize());
            }
            wrapper.lambda().in(Note::getId, noteIds);
        }

        IPage<Note> result = this.page(page, wrapper);
        Map<String, User> userMap = iUserService.getByIds(result.getRecords().stream().map(Note::getUserId).collect(Collectors.toList()))
                .stream().collect(Collectors.toMap(User::getId, Function.identity()));
        List<String> notesLike = iUserNoteService.listLikes(result.getRecords().stream().map(Note::getId).collect(Collectors.toList()));

        return result.convert(n -> {
            NoteVO vo = new NoteVO(n);
            User user = userMap.getOrDefault(n.getUserId(), new User());
            vo.setUserImg(user.getProfileImg());
            vo.setUserAccount(user.getAccount());
            vo.setUserName(user.getName());
            vo.setIsLiked(notesLike.contains(n.getId()));
            return vo;
        });
    }

    @Override
    public IPage<NoteAdminVO> page(NoteAdminPageSearch search) {
        QueryWrapper<Note> wrapper = new QueryWrapper<>();
        wrapper.lambda().orderByDesc(Note::getUpdateTime);
        if (!StringUtils.isEmpty(search.getChannelId())) {
            wrapper.lambda().eq(Note::getChannelId, search.getChannelId());
        }
        if (!StringUtils.isEmpty(search.getTitle())) {
            wrapper.lambda().like(Note::getTitle, search.getTitle());
        }
        if ((!StringUtils.isEmpty(search.getUserAccount())) || (!StringUtils.isEmpty(search.getUserName()))) {
            List<String> userIds = iUserService.listByNameAndAccount(search.getUserName(), search.getUserAccount());
            if (CollectionUtils.isEmpty(userIds)) {
                return new Page<>(search.getCurrentPage(), search.getPageSize());
            } else {
                wrapper.lambda().in(Note::getUserId, userIds);
            }
        }

        IPage<Note> result = this.page(new Page<>(search.getCurrentPage(), search.getPageSize()), wrapper);
        Map<String, User> userMap = iUserService.getByIds(result.getRecords().stream().map(Note::getUserId).collect(Collectors.toList()))
                .stream().collect(Collectors.toMap(User::getId, Function.identity()));

        return result.convert(n -> {
            NoteAdminVO vo = new NoteAdminVO(n);
            User user = userMap.getOrDefault(n.getUserId(), new User());
            vo.setUserAccount(user.getAccount());
            vo.setUserName(user.getName());
            return vo;
        });
    }

    @Override
    public NoteVO detail(String noteId, boolean isEdit) {
        String userId = currentLoginUserService.getUserId(true);
        Note note = this.getById(noteId);
        if (note == null) {
            throw new BusinessException("笔记不存在");
        }
        if (isEdit) {
            if (!userId.equals(note.getUserId())) {
                throw new BusinessException("该笔记属于其他用户");
            }
            return new NoteVO(note);
        } else {
            if (Boolean.FALSE.equals(note.getIsPublish())) {
                throw new BusinessException("该笔记未发布");
            } else {
                NoteVO vo = new NoteVO(note);
                User user = iUserService.getById(note.getUserId());
                if (user != null) {
                    vo.setUserAccount(user.getAccount());
                    vo.setUserName(user.getName());
                    vo.setUserImg(user.getProfileImg());
                    vo.setIsFollowed(iFollowService.isFollowed(userId, note.getUserId()));
                }
                Map<String, Boolean> likeCollMap = iUserNoteService.getLikeCollMap(noteId);
                vo.setIsLiked(likeCollMap.getOrDefault("like", false));
                vo.setIsCollected(likeCollMap.getOrDefault("collect", false));
                return vo;
            }
        }

    }

    @Override
    public void likeNum(String noteId, int i) {
        Note note = this.getById(noteId);
        if (note != null) {
            note.setLikeNum(note.getLikeNum() + i);
            this.updateById(note);
        }
    }

    @Override
    public void collectNum(String noteId, int i) {
        Note note = this.getById(noteId);
        if (note != null) {
            note.setCollectNum(note.getCollectNum() + i);
            this.updateById(note);
        }
    }

    @Override
    public void remarkNum(String noteId, int i) {
        Note note = this.getById(noteId);
        if (note != null) {
            note.setRemarkNum(note.getRemarkNum() + i);
            this.updateById(note);
        }
    }

    @Override
    public IPage<NoteVO> pageDraft(PageBeanSearch search) {
        String userId = currentLoginUserService.getUserId(true);
        IPage<Note> page = new Page<>(search.getCurrentPage(), search.getPageSize());
        QueryWrapper<Note> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Note::getIsPublish, false)
                .eq(Note::getUserId, userId)
                .orderByDesc(Note::getCreateTime);
        return this.page(page, wrapper).convert(NoteVO::new);
    }

    @Override
    public List<Note> listByUserId(String userId) {
        QueryWrapper<Note> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Note::getIsPublish, true)
                .eq(Note::getUserId, userId);
        return this.list(wrapper);
    }

    @Override
    public List<NoteVO> listHeat(String channelId, int size) {
        IPage<Note> page = new Page<>(1, size);
        QueryWrapper<Note> wrapper = new QueryWrapper<>();
        wrapper.lambda().last("ORDER BY (like_num + collect_num + remark_num) DESC")
                .eq(Note::getIsPublish, true);
        if (!StringUtils.isEmpty(channelId)) {
            wrapper.lambda().eq(Note::getChannelId, channelId);
        }
        return this.page(page, wrapper).convert(note -> {
            NoteVO vo = new NoteVO(note);
            vo.setHeat(vo.getLikeNum() + vo.getCollectNum() + vo.getRemarkNum());
            return vo;
        }).getRecords();
    }

    @Override
    public List<SimpleVO> noteChannel() {
        return this.baseMapper.noteChannel();
    }

    @Override
    public void removeNote(String noteId) {
        this.removeById(noteId);
        iUserNoteService.removeByNoteId(noteId);
    }

    @Override
    public NoteAdminDetailVO detailNote(String id) {
        Note note = this.getById(id);
        if (note == null) {
            throw new BusinessException("笔记不存在");
        }

        NoteAdminDetailVO vo = new NoteAdminDetailVO(note);
        User user = iUserService.getById(note.getUserId());
        if (user != null) {
            vo.setUserAccount(user.getAccount());
            vo.setUserName(user.getName());
        }
        Channel channel = iChannelService.getById(note.getChannelId());
        if (channel != null) {
            vo.setChannelName(channel.getName());
        }
        return vo;
    }

    @Override
    public void add(NoteAddDTO dto, boolean isPublish) {
        String userId = currentLoginUserService.getUserId(true);
        if (StringUtils.isEmpty(dto.getId())) {
            Note note = new Note(dto);
            note.setLikeNum(0)
                    .setIsPublish(isPublish)
                    .setCollectNum(0)
                    .setRemarkNum(0)
                    .setUserId(userId);
            this.save(note);
        } else {
            Note note = this.getById(dto.getId());
            if (note == null) {
                throw new BusinessException("旧笔记不存在");
            } else if (!userId.equals(note.getUserId())) {
                throw new BusinessException("该笔记属于其他用户");
            }
            note.setIsPublish(isPublish)
                    .setImgs(JSON.toJSONString(dto.getImgs()))
                    .setChannelId(dto.getChannelId())
                    .setText(dto.getText())
                    .setTitle(dto.getTitle());
            this.updateById(note);
        }
    }
}
